package report

import (
	"file-cleaner/internal/file_cleaner/clean_handler"
	"fmt"
	v1log "github.com/ClessLi/component-base/pkg/log/v1"
	"time"
)

type Reporter interface {
	ReportFromResults(cleanResult clean_handler.CleanTaskStatisticsResult, during time.Duration)
}

type LogInfoReporterForDryRun struct {
}

func NewDryRunReporter() Reporter {
	return Reporter(new(LogInfoReporterForDryRun))
}

func (d LogInfoReporterForDryRun) ReportFromResults(cleanResult clean_handler.CleanTaskStatisticsResult, during time.Duration) {
	v1log.Infof(
		"本次操作总共耗时：%s\n"+
			"\t结果概览：\n"+
			"\t\t总文件数量：%d 个，\t干跑文件数量：共 %d 个，\t成功 %d 个，\t失败 %d 个；\n"+
			"\t\t总文件大小：%s，\t干跑处理文件大小：共 %s，\t成功 %s，\t失败 %s；",

		during,

		cleanResult.TotalFilesCount, cleanResult.NeedHandledFilesCount, cleanResult.SucceedHandledFilesCount, cleanResult.NeedHandledFilesCount-cleanResult.SucceedHandledFilesCount,
		humanReadableSize(float64(cleanResult.TotalFilesSize)), humanReadableSize(float64(cleanResult.NeedHandledFilesSize)), humanReadableSize(float64(cleanResult.SucceedHandledFilesSize)), humanReadableSize(float64(cleanResult.NeedHandledFilesSize-cleanResult.SucceedHandledFilesSize)),
	)
}

type LogInfoReporterForLink struct {
}

func NewLinkReporter() Reporter {
	return Reporter(new(LogInfoReporterForLink))
}

func (l LogInfoReporterForLink) ReportFromResults(cleanResult clean_handler.CleanTaskStatisticsResult, during time.Duration) {
	v1log.Infof(
		"本次操作总共耗时：%s\n"+
			"\t结果概览：\n"+
			"\t\t总文件数量：%d 个，\t清理重复文件及创建硬链接数量：共 %d 个，\t成功 %d 个，\t失败 %d 个；\n"+
			"\t\t总文件大小：%s，\t清理重复文件大小：共 %s，\t成功 %s，\t失败 %s；\n"+
			"\t\t文件成功去重比：%.2f%%，\t空间成功优化比：%.2f%%。",

		during,

		cleanResult.TotalFilesCount, cleanResult.NeedHandledFilesCount, cleanResult.SucceedHandledFilesCount, cleanResult.NeedHandledFilesCount-cleanResult.SucceedHandledFilesCount,
		humanReadableSize(float64(cleanResult.TotalFilesSize)), humanReadableSize(float64(cleanResult.NeedHandledFilesSize)), humanReadableSize(float64(cleanResult.SucceedHandledFilesSize)), humanReadableSize(float64(cleanResult.NeedHandledFilesSize-cleanResult.SucceedHandledFilesSize)),
		float32(cleanResult.SucceedHandledFilesCount)/float32(cleanResult.TotalFilesCount)*100, float64(cleanResult.SucceedHandledFilesSize)/float64(cleanResult.TotalFilesSize)*100,
	)
}

type LogInfoReporterForDelete struct {
}

func NewDeleteReporter() Reporter {
	return Reporter(new(LogInfoReporterForDelete))
}

func (r LogInfoReporterForDelete) ReportFromResults(cleanResult clean_handler.CleanTaskStatisticsResult, during time.Duration) {
	v1log.Infof(
		"本次操作总共耗时：%s\n"+
			"\t结果概览：\n"+
			"\t\t总文件数量：%d 个，\t清理文件数量：共 %d 个，\t成功 %d 个，\t失败 %d 个；\n"+
			"\t\t总文件大小：%s，\t清理文件大小：共 %s，\t成功 %s，\t失败 %s；\n"+
			"\t\t空间成功优化比：%.2f%%。",

		during,

		cleanResult.TotalFilesCount, cleanResult.NeedHandledFilesCount, cleanResult.SucceedHandledFilesCount, cleanResult.NeedHandledFilesCount-cleanResult.SucceedHandledFilesCount,
		humanReadableSize(float64(cleanResult.TotalFilesSize)), humanReadableSize(float64(cleanResult.NeedHandledFilesSize)), humanReadableSize(float64(cleanResult.SucceedHandledFilesSize)), humanReadableSize(float64(cleanResult.NeedHandledFilesSize-cleanResult.SucceedHandledFilesSize)),
		float64(cleanResult.SucceedHandledFilesSize)/float64(cleanResult.TotalFilesSize)*100,
	)

}

const (
	sizeInByte = iota
	sizeInKiloByte
	sizeInMegaByte
	sizeInGigaByte
	sizeInTeraByte
	sizeInPetaByte
	sizeInExaByte
)

func humanReadableSize(n float64) string {
	level := sizeInByte
	hs, level := calculateHumanReadableSize(n, level)
	u := calculateUnits(level)
	return fmt.Sprintf("%.3f%s", hs, u)
}

func calculateUnits(level int) string {
	switch level {
	case sizeInByte:
		return "B"
	case sizeInKiloByte:
		return "KB"
	case sizeInMegaByte:
		return "MB"
	case sizeInGigaByte:
		return "GB"
	case sizeInTeraByte:
		return "TB"
	case sizeInPetaByte:
		return "PB"
	case sizeInExaByte:
		return "EB"
	default:
		return "??"
	}
}

func calculateHumanReadableSize(n float64, l int) (float64, int) {
	if l < sizeInExaByte {
		c := n / 1024
		if c < 1 {
			return n, l
		}
		l += 1
		return calculateHumanReadableSize(c, l)
	}
	return n, l
}
